﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Input;

using MyUtils;
using MyUtils.UI;

using XTrace.Viewer.Filter;

namespace XTrace.Viewer
{
	class StringSetFilterViewModel : ObservableObject
	{
		public StringSetFilterViewModel(string fieldName)
		{
			if (fieldName.IsNullOrEmpty())
			{
				throw new ArgumentNullException("fieldName");
			}
			_fieldName = fieldName;
			_entriesFiltered.Source = EntriesNonFiltered;
			_entriesFiltered.Filter += EntriesFilteredFilter;

			_fieldGetter = new Lazy<Func<XTraceEvent, string>>(() => typeof(XTraceEvent).GetProperty(_fieldName).GetGetMethod().Compile<Func<XTraceEvent, string>>());

			(_entries as INotifyPropertyChanged).PropertyChanged += (s, e) => OnPropertyChanged("AnyUseful");
		}

		readonly string _fieldName;

		public override string ToString()
		{
			return FieldName;
		}

		FilterModel _filterModel;

		public FilterModel FilterModel
		{
			get { return _filterModel; }
			set
			{
				var npcOld = StringSetFilterModel as INotifyPropertyChanged;
				if (npcOld != null)
				{
					npcOld.PropertyChanged -= Propagate;
				}

				_filterModel = value;
				SyncEntries();

				var npcNew = StringSetFilterModel as INotifyPropertyChanged;
				if (npcNew != null)
				{
					npcNew.PropertyChanged -= Propagate;
					npcNew.PropertyChanged += Propagate;
				}

				OnPropertyChanged("FilterModel");
				OnPropertyChanged("StringSetFilterModel");
			}
		}

		void Propagate(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName.IsNullOrWhitespaces() || e.PropertyName == "IsActuallyAll")
			{
				OnPropertyChanged("AnyUseful");
			}
		}

		public StringSetFilter StringSetFilterModel
		{
			get
			{
				var fm = FilterModel;
				var res = fm == null ? null : fm.GetStringSetFilter(FieldName);
				return res;
			}
		}

		void SyncEntries()
		{
			// load new categories
			var ssfm = StringSetFilterModel;
			if (ssfm != null)
			{
				var newItems = ssfm.IncludesHashSet.Except(_entriesSet).Union(ssfm.ExcludesHashSet.Except(_entriesSet));
				foreach (var newItem in newItems)
				{
					AddEntry(newItem);
				}
			}
			// update state
			foreach (var entryViewModel in EntriesNonFiltered)
			{
				entryViewModel.Notify();
			}
		}

		readonly HashSet<string> _entriesSet = new HashSet<string>();

		private readonly ObservableCollection<StringSetFilterEntryViewModel> _entries = new ObservableCollection<StringSetFilterEntryViewModel>();

		public void AddEntry(string entry)
		{
			if (entry == null)
			{
				entry = string.Empty;
			}
			if (_entriesSet.Add(entry))
			{
				EntriesNonFiltered.Add(new StringSetFilterEntryViewModel(entry, this));
			}
		}

		public string FieldName
		{
			get { return _fieldName; }
		}

		readonly Lazy<Func<XTraceEvent, string>> _fieldGetter;
		public Func<XTraceEvent, string> FieldGetter
		{
			get { return _fieldGetter.Value; }
		}

		#region Quick Search

		private string _entryQuickSearch;

		public string EntryQuickSearch
		{
			get { return _entryQuickSearch; }
			set
			{
				_entryQuickSearch = value;
				OnPropertyChanged("EntryQuickSearch");
				_entriesFiltered.View.Refresh();
			}
		}

		void EntriesFilteredFilter(object sender, FilterEventArgs e)
		{
			var entry = (StringSetFilterEntryViewModel)e.Item;
			e.Accepted = string.IsNullOrEmpty(_entryQuickSearch) || entry.Name.ToUpperInvariant().Contains(_entryQuickSearch.ToUpperInvariant());
		}

		private readonly CollectionViewSource _entriesFiltered = new CollectionViewSource();

		public ICommand FilterClearQuickSearch
		{
			get { return new DelegateCommand(x => EntryQuickSearch = null); }
		}

		public IEnumerable Entries
		{
			get
			{
				return _entriesFiltered.View;
			}
		}

		public ObservableCollection<StringSetFilterEntryViewModel> EntriesNonFiltered
		{
			get { return _entries; }
		}

		public bool AnyUseful
		{
			get { return EntriesNonFiltered.Count > 1 || !StringSetFilterModel.IsActuallyAll; }
		}

		public void Clear()
		{
			_entries.Clear();
			_entriesSet.Clear();
			SyncEntries();
		}

		#endregion
	}
}
